﻿#pragma once
#include "Buffer.h"

namespace COMM
{
	class COMM_API JsonNode
	{
	protected:
		JsonNode();
	public:
		virtual ~JsonNode();
		
		// operator
		bool operator==(const JsonNode& rhs) const;
		bool operator!=(const JsonNode& rhs) const;
		JsonNode& operator=(const char* value);
		JsonNode& operator=(bool value);
		JsonNode& operator=(double value);
		JsonNode& operator=(unsigned char value);
		JsonNode& operator=(int value);
		JsonNode& operator=(long long value);

		// add empty object node if indexed node does not exist.
		JsonNode operator[](int index);
		// add empty object node if the named node was not exist
		JsonNode operator[](const char* name);
		// add empty array node if indexed node does not exist.
		JsonNode operator()(int index);
		// add empty array node if the named node was not exist
		JsonNode operator()(const char* name);

		operator String() const;
		operator bool() const;
		operator double() const;
		operator unsigned char() const;
		operator int() const;
		operator long long() const;

		// reset the type and value of current node.
		JsonNode& Set(const char* value);
		JsonNode& Set(bool value);
		JsonNode& Set(double value);
		JsonNode& Set(unsigned char value);
		JsonNode& Set(int value);
		JsonNode& Set(long long value);

		// check
		bool IsNull() const;
		bool IsString() const;
		bool IsBool() const;
		bool IsNumber() const;
		bool IsArray() const;
		bool IsObject() const;

		// convert
		bool AsBool() const;
		String AsString() const;	// return String::Empty if not string and not object, return json string if is object.
		double AsNumber() const;	// return NAN if not number.
		unsigned char AsByte() const;
		int AsInt() const;
		long long AsLong() const;

		// query
		const char* Name() const;
		bool Has(const char* name) const;
		JsonNode Get(const char* name) const;	// return null object if not exist.
		JsonNode Get(int index) const;			// return null object if not exist.
		int Count() const;

		// remove
		JsonNode& Remove(const char* name);
		JsonNode& Remove(int index);

		// add sub array node. convert current node to array node automatic if current node is not a array node.
		JsonNode& Add(const char* value); 
		JsonNode& Add(bool value);
		JsonNode& Add(double value);
		JsonNode& Add(unsigned char value);
		JsonNode& Add(int value);
		JsonNode& Add(long long value);
		// insert sub array node. convert current node to array node automatic if current node is not a array node.
		JsonNode& Insert(int index, const char* value);
		JsonNode& Insert(int index, bool value);
		JsonNode& Insert(int index, double value);
		JsonNode& Insert(int index, unsigned char value);
		JsonNode& Insert(int index, int value);
		JsonNode& Insert(int index, long long value);

		// add sub object node.
		JsonNode& Add(const char* name, const char* value);
		JsonNode& Add(const char* name, bool value);
		JsonNode& Add(const char* name, double value);
		JsonNode& Add(const char* name, unsigned char value);
		JsonNode& Add(const char* name, int value);
		JsonNode& Add(const char* name, long long value);

		// add new array node.
		JsonNode AddArray(const char* name = NULL); 
		// add new object node.
		JsonNode AddObject(const char* name = NULL);


		String ToJson(bool format = false) const; // to json text.
		bool Save(const char* filename, bool format = true) const;

	protected:
		JsonNode& Add(const JsonNode& node);
		JsonNode& Add(const char* name, const JsonNode& node);
		JsonNode& Insert(int index, const JsonNode& node);
		long double GetValue() const; 
		JsonNode& SetValue(long double value);
		void ResetValue();
	protected:
		void* m_monitor;
	};

	// JsonRoot used for release buffer automatic.
	//
	class COMM_API JsonRoot : public JsonNode
	{
		MYLIB_NONCOPIED_CLASS(JsonRoot);
	public:
		JsonRoot();
		JsonRoot(const char* strorpath, bool ispath = false);
		~JsonRoot();
	};

}

#define MYLIB_JSON2CV(root,ctp,cname,cval)	\
	ctp cname = cval; \
	do{	\
		JsonNode node_ = root.Get(#cname);	\
		String stp_ = #ctp;	\
		stp_.trim(); \
		if (node_.IsNull()) break;	\
		if(stp_ == "String"){	\
			if(!node_.IsString()) break;	\
		}else if(stp_ == "int" || stp_ == "double" || stp_ == "long long" || stp_ == "unsigned char"){	\
			if(!node_.IsNumber()) break;	\
		}else if(stp_ == "bool"){	\
			if(!node_.IsBool()) break;	\
		}else{	\
			break;	\
		}	\
		cname = node_;	\
	} while(false);	

